#include "d3d9/d3d9mesh.h"
#include "mesh.h"

using namespace Kraw;

////////////////////////////////////////////////////////////////////////////////
/// geometry

D3D9Mesh::D3D9Mesh( D3D9Display* display ) :
    Mesh(display),
    pVertexBuffer(0),
    pIndexBuffer(0),
    vertexStride(0),
    vertexFormat(0),
    numPrimitives(0)
{
    pD3D9Device = display->pD3D9Device;
}

/// TODO- actually look into the list of registered meshes
/// looking for one with a matching descriptor (see Forsyth doc)
Mesh* Display::LoadMesh( MeshSource* src )
{
    D3D9Display* self = (D3D9Display*) this;

    //TODO- lookup src->GetName() in internal cache, reuse if it is already loaded

    if ( !src->Load() )
        return 0;

    //load vertex buffer
    D3D9Mesh* mesh = new D3D9Mesh( self );
    mesh->numVertices = src->numVertices;
    mesh->numIndices = src->numIndices;
    mesh->numPrimitives = src->numIndices / 3;
    mesh->vertexStride = src->GetVertexWidth() * sizeof(float);

    //TODO- get vertex format from streams
    mesh->vertexFormat = 0;
    for (int streamIdx = 0; streamIdx < src->numStreams; ++streamIdx)
        switch (src->pStreams[streamIdx].usage)
        {
        case MeshSource::Coord:
            mesh->vertexFormat |= D3DFVF_XYZ;
            break;
        case MeshSource::TexCoord:
            mesh->vertexFormat |= D3DFVF_TEX0 + src->pStreams[streamIdx].index;
            break;
        case MeshSource::Diffuse:
            mesh->vertexFormat |= D3DFVF_DIFFUSE;
            break;
        case MeshSource::Normal:
            mesh->vertexFormat |= D3DFVF_NORMAL;
            break;
        default:
            //stream not supported
            delete mesh;
            return 0;
        }

    int vertexSize = src->numVertices * mesh->vertexStride; //size in bytes

    HRESULT hr;
    hr = self->pD3D9Device->CreateVertexBuffer( vertexSize, 0, mesh->vertexFormat, 
        D3DPOOL_DEFAULT, &mesh->pVertexBuffer, NULL );

    if ( SUCCEEDED( hr ) )
    {
        void* pVertices;
        hr = mesh->pVertexBuffer->Lock( 0, vertexSize, (void**) &pVertices, 0 );
        if ( SUCCEEDED( hr ) )
        {
            memcpy( pVertices, src->pVertices, vertexSize );
            mesh->pVertexBuffer->Unlock();
        }
    }
    
    //load index buffer
    if ( SUCCEEDED( hr ) )
    {
        hr = self->pD3D9Device->CreateIndexBuffer( mesh->numIndices, 0, D3DFMT_INDEX32,
            D3DPOOL_DEFAULT, &mesh->pIndexBuffer, NULL );
        
        if ( SUCCEEDED( hr ) )
        {
            int* indices;
            hr = mesh->pIndexBuffer->Lock( 0, mesh->numIndices, (void**) &indices, 0 );
            if ( SUCCEEDED( hr ) )
            {
                memcpy( indices, src->pIndices, mesh->numIndices * sizeof(int) );
                mesh->pIndexBuffer->Unlock();
            }
        }
    }

    src->Unload();

    if ( SUCCEEDED( hr ) )
        return mesh;

    delete mesh;
    return 0;
}

/// immediate vertex rendering
bool Display::DrawMesh( Mesh* msh )
{
    D3D9Display* self = (D3D9Display*) this;
    D3D9Mesh* mesh = (D3D9Mesh*) msh;

	self->SetTransforms();
    
    self->pD3D9Device->SetStreamSource( 0, mesh->pVertexBuffer, 0, mesh->vertexStride );
    self->pD3D9Device->SetFVF( mesh->vertexFormat );
    self->pD3D9Device->SetIndices( mesh->pIndexBuffer );
    self->pD3D9Device->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, mesh->numVertices, 0, mesh->numPrimitives );

    return true;
}
